# -*- coding: utf-8 -*
import datetime

from influxdb import InfluxDBClient
from typing import List
from utils.gen import generate_data_key
from utils.enums import PastTime, DeviceName
from utils.time import handle_standard_time


class DataBase:
    """时序数据库"""

    def __init__(self, retention_time="1h", dbname="datadb", host="localhost", port=8086):
        self.client = InfluxDBClient(host=host, port=port)
        try:
            dbs = self.client.get_list_database()
        except Exception:
            raise ConnectionError("You should install or configure influxdb service firstly!")

        is_exist = False
        for db in dbs:
            if db["name"] == dbname:
                is_exist = True
                break

        if not is_exist:
            self.client.create_database(dbname)
        self.client.switch_database(dbname)

        try:
            self.client.create_retention_policy(name="1_hour", duration=retention_time, replication="1",
                                                database=dbname, default=True)
        except Exception:
            self.client.drop_retention_policy(name="1_hour", database=dbname)
            self.client.create_retention_policy(name="1_hour", duration=retention_time, replication="1",  # 修改保留策略的时候改这里
                                                database=dbname, default=True)

    def insert(self, data: List[float], device_name: DeviceName):
        """插入数据"""
        if not isinstance(device_name, DeviceName):
            raise TypeError("Type of device_name must be 'DeviceName'!")

        cur_time = datetime.datetime.now().isoformat()
        body = [
            {
                "measurement": device_name.name,
                "time": cur_time,
                "fields": generate_data_key(device_name)
            }
        ]

        for i, metric in enumerate(data):
            for j, key in enumerate(body[0]["fields"]):
                if i == j:
                    body[0]["fields"][key] = metric

        res = self.client.write_points(body)
        if not res:
            raise Exception("Insert error!")

    def query(self, device_name: DeviceName, past_time: PastTime = PastTime.NOW, fields: List[str] = ["*"], debug=False):
        """查询数据"""
        if not isinstance(device_name, DeviceName):
            raise TypeError("Type of device_name must be 'DeviceName'!")

        if len(fields) == 1:
            field = fields[0]
        else:
            field = ",".join(fields)

        clause = "SELECT {} FROM {} ".format(field, device_name.name)

        bind_params = {}

        if not isinstance(past_time, PastTime):
            raise TypeError("Type of past_time must be 'PastTime'!")
        if debug:
            cond = ""
        else:
            if past_time == PastTime.NOW:
                cond = 'ORDER BY time DESC LIMIT 1;'
                bind_params = {}
            else:
                raw_time = datetime.datetime.now().isoformat()
                cur_time = handle_standard_time(raw_time, past_time)
                bind_params = {
                    'cur_time': cur_time,
                }
                cond = 'WHERE time >= $cur_time'

        clause = clause + cond
        rsp = self.client.query(clause, bind_params=bind_params)
        points = list(rsp.get_points(measurement=device_name.name))
        return points

    def drop(self, dbname="datadb"):
        """删除数据库，debug时使用"""
        self.client.drop_database(dbname)
